﻿//     Copyright (c) Microsoft Corporation.  All rights reserved.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using Gateway.Console.Models;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using Microsoft.WindowsAzure;
using System.Net;
using System.Net.Http.Headers;

namespace Gateway.Console.Services.Azure
{
    public class AzureManagement : IDisposable
    {
        protected ConsoleContext _database = new ConsoleContext();
        protected AuthenticationResult _accessToken;

        private AzureManagement(AuthenticationResult accessToken, string subscriptionId)
        {
            this._hdInsight = new Lazy<HDInsightManagement>(() => new HDInsightManagement(accessToken, subscriptionId, String.Empty, this._database));
            this._scheduler = new Lazy<SchedulerManagement>(() => new SchedulerManagement(accessToken, subscriptionId, this._database));
            this._accessToken = accessToken;
        }

        private AzureManagement(AuthenticationResult accessToken, string subscriptionId, string clusterName) 
            : this(accessToken, subscriptionId)
        {
            this._hdInsight = new Lazy<HDInsightManagement>(() => new HDInsightManagement(accessToken, subscriptionId, clusterName, this._database));
        }

        private Lazy<HDInsightManagement> _hdInsight;
        public HDInsightManagement HDInsight
        {
            get { return _hdInsight.Value; }
        }

        private Lazy<SchedulerManagement> _scheduler;
        public SchedulerManagement Scheduler
        {
            get { return _scheduler.Value; }
        }

        private static void InvokeAction(AnalyticsModel model, 
            AuthenticationResult accessToken, 
            string subscriptionId, 
            Func<AzureManagement, AnalyticsModel, Task> action)
        {
            Task.Factory.StartNew(analyticsModel =>
            {
                using (var manager = new AzureManagement(accessToken, subscriptionId, ((AnalyticsModel)analyticsModel).ClusterName))
                {
                    action(manager, (AnalyticsModel)analyticsModel).Wait();
                }
            }, model);
        }

        public static void CreateAnalytics(AnalyticsModel model, AuthenticationResult accessToken, string subscriptionId)
        {
            InvokeAction(model, accessToken, subscriptionId, async (manager, analytics) => await manager.CreateAnalyticsCapabilityAsync(analytics));
        }

        public static void DeleteAnalytics(AnalyticsModel model, AuthenticationResult accessToken, string subscriptionId)
        {
            InvokeAction(model, accessToken, subscriptionId, async (manager, analytics) => await manager.DestroyAnalyticsCapabilityAsync(analytics));
        }

        public static void UpdateAnalytics(AnalyticsModel model, AuthenticationResult accessToken, string subscriptionId)
        {
            InvokeAction(model, accessToken, subscriptionId, async (manager, analytics) => await manager.CreateOrUpdateAnalyticsObjectsAsync(analytics, false));
        }

        public static void CalculateAggregates(AnalyticsModel model, AuthenticationResult accessToken, string subscriptionId)
        {
            InvokeAction(model, accessToken, subscriptionId, async (manager, analytics) => await manager.HDInsight.CalculateAggregatesAsync(analytics));
        }

        public async Task<bool> CreateAnalyticsCapabilityAsync(AnalyticsModel model)
        {
            if (!await this.HDInsight.CreateClusterAsync(model))
            {
                return false;
            }
            return await CreateOrUpdateAnalyticsObjectsAsync(model, true);
        }

        public async Task<bool> DestroyAnalyticsCapabilityAsync(AnalyticsModel model)
        {
            await this.Scheduler.DeleteAggregateJobsAsync(model);
            await this.HDInsight.DestroyCluster(model);
            return true;
        }

        public async Task<bool> CreateOrUpdateAnalyticsObjectsAsync(AnalyticsModel model, bool create)
        {
            if (!await this.HDInsight.CreateOrUpdateHiveMetadataAsync(model))
            {
                return false;
            }
            else if (!await this.Scheduler.CreateOrUpdateAggregateJobsAsync(model, create))
            {
                return false;
            }
            else if (!await this.HDInsight.UpdateIISLogTablePartitionsAsync(model))
            {
                return false;
            }
            WriteStatus<AnalyticsStatus>(model.Id, "Calculating aggregates on existing logs", true);
            await this.HDInsight.CalculateAggregatesAsync(model);
            WriteStatus<AnalyticsStatus>(model.Id, "Finished Deployment", true);

            return true;
        }

        private void WriteStatus<TStatus>(int modelId, string message, bool success) where TStatus : Status, new()
        {
            System.Diagnostics.Trace.TraceInformation("{0} {1} {2}", DateTime.UtcNow, success, message);
            this._database.AddStatus<TStatus>(modelId, DateTime.UtcNow, success, message);
        }

        public void Dispose()
        {
            if (this._hdInsight.IsValueCreated)
            {
                this.HDInsight.Dispose();
            }
            if (this._scheduler.IsValueCreated)
            {
                this.Scheduler.Dispose();
            }
            this._database.Dispose();
        }
    }
}